﻿// Groupon.NET C# Library
// Copyright (c) 2011 James Paul Duncan (JPaulDuncan)
// The MIT License (MIT)
// Permission is hereby granted, free of charge, to any person obtaining a 
// copy of this software and associated documentation files (the "Software"), 
// to deal in the Software without restriction, including without limitation 
// the rights to use, copy, modify, merge, publish, distribute, sublicense, 
// and/or sell copies of the Software, and to permit persons to whom the 
// Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included 
// in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
// DEALINGS IN THE SOFTWARE. 
// http://www.jpaulduncan.com

using System;
using System.Collections.Generic;
using System.Net;

namespace Groupon.Net
{
    /// <summary>
    /// Groupon Service Client
    /// </summary>
    public sealed class Client
    {

        public static event EventHandler<ClientExceptionEventArgs> ClientException;

        /// <summary>
        /// private default constructor
        /// </summary>
        private Client()
        {
            this.DealProvider = new DealProvider();
            this.DivisionProvider = new DivisionProvider();
            this.PostProvider = new PostsProvider();
            this.GrouponSayingsProvider = new GrouponSayingsProvider();
            this.CommissionJunctionProvider = new CommissionJunctionProvider();
        }

        /// <summary>
        /// Groupon API Key (client_id)
        /// </summary>
        public static string ClientID { get; set; }

        public static string CommissionJunctionReferralUrl { get; set; }

        /// <summary>
        /// Location of the Groupon API
        /// </summary>
        public static Uri APIUri { get { return new Uri(string.Format(@"http://api.groupon.com/{0}/", Client.APIVersion)); } }

        /// <summary>
        /// Groupon API Version used by this Client.
        /// </summary>
        public static string APIVersion { get { return "v2"; } }

        /// <summary>
        /// The version of this assembly.
        /// </summary>
        public static Version Version { get { return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; } }

        /// <summary>
        /// The User-Agent string sent to Groupon.
        /// </summary>
        public static string UserAgent { get { return string.Format(@"Groupon.Net Library/{0}", Groupon.Net.Client.Version.ToString()); } }

        /// <summary>
        /// Proxy for the service client.
        /// </summary>
        public static IWebProxy Proxy { get; set; }

        /// <summary>
        /// Credentials for the client.
        /// </summary>
        public static NetworkCredential Credentials { get; set; }

        /// <summary>
        /// Shows detailed information about a specified deal
        /// </summary>
        /// <param name="dealId">Deal identifier</param>
        /// <returns></returns>
        public static Deal GetDeal(string dealId) { return Client.GetDeal(dealId, string.Empty, string.Empty, ShowTypes.Default, string.Empty, null); }

        /// <summary>
        /// Shows detailed information about a specified deal
        /// </summary>
        /// <param name="dealId">Deal identifier</param>
        /// <param name="googleAnalytics"></param>
        /// <returns></returns>
        public static Deal GetDeal(string dealId, GoogleAnalytics googleAnalytics) { return Client.GetDeal(dealId, string.Empty, string.Empty, ShowTypes.Default, string.Empty, googleAnalytics); }

        /// <summary>
        /// Shows detailed information about a specified deal
        /// </summary>
        /// <param name="dealId">Deal identifier</param>
        /// <param name="emailAddress">When not logged in, this can be used to load targeted deal content for a user.</param>
        /// <param name="area">For divisions that have sub-areas, specify an area permalink to retrieve those deals.</param>
        /// <param name="show">Determines which values should be returned</param>
        /// <param name="referralId">The uuXXXXX referral id for a Groupon user account. If provided, deal links returned by the API will include the proper UTM parameters to ensure that any purchases from that URL will be credited to that user's account.</param>
        /// <param name="googleAnalytics"></param>
        /// <returns></returns>
        public static Deal GetDeal(string dealId, string emailAddress, string area, ShowTypes show, string referralId, GoogleAnalytics googleAnalytics)
        {

            return Client.Instance.DealProvider.GetDeal(dealId, emailAddress, area, show, referralId, googleAnalytics);
        }

        /// <summary>
        /// Show a list of all deals for a division
        /// </summary>
        /// <returns></returns>
        public static List<Deal> GetDeals() { return Client.GetDeals(null, 0, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, ShowTypes.Default, string.Empty); }

        /// <summary>
        /// Show a list of all deals for a division
        /// </summary>
        /// <param name="divisionId">Division identifier</param>
        /// <returns></returns>
        public static List<Deal> GetDeals(string divisionId) { return Client.GetDeals(null, 0, divisionId, string.Empty, string.Empty, string.Empty, string.Empty, ShowTypes.Default, string.Empty); }

        /// <summary>
        /// Show a list of all deals for a division
        /// </summary>
        /// <param name="divisionId">Division identifier</param>
        /// <param name="show">Determines which values should be returned</param>
        /// <returns></returns>
        public static List<Deal> GetDeals(string divisionId, ShowTypes show) { return Client.GetDeals(null, 0, divisionId, string.Empty, string.Empty, string.Empty, string.Empty, show, string.Empty); }

        /// <summary>
        /// Show a list of all deals for a division
        /// </summary>
        /// <param name="geoCode">The longitude and latitude of an area.</param>
        /// <param name="radius">Distance (in miles) from the provided lat / lng within which to locate deals</param>
        /// <param name="divisionId">Division identifier</param>
        /// <param name="area">For divisions that have sub-areas, specify an area permalink to retrieve those deals.</param>
        /// <param name="emailAddress">When not logged in, this can be used to load targeted deal content for a user.</param>
        /// <param name="deviceToken">When not logged in, this can be used to load targeted deal content based on client device token.</param>
        /// <param name="subscriberId">When not logged in, a subscription center id can be passed in this parameter to load targeted deals for that email address. A logged-in user or email_address parameter will take precedence over this value.</param>
        /// <param name="show">Determines which values should be returned</param>
        /// <param name="affiliateId">If an affiliate id is passed in the affiliate parameter, the /deals response will contain only deals for that affiliate</param>
        /// <returns></returns>
        public static List<Deal> GetDeals(GeoCode geoCode, int radius, string divisionId, string area, string emailAddress, string deviceToken, string subscriberId, ShowTypes show, string affiliateId)
        {
            return Client.Instance.DealProvider.GetDeals(geoCode, radius, divisionId, area, emailAddress, deviceToken, subscriberId, show, affiliateId);
        }

        /// <summary>
        /// Show a list of all launched divisions.
        /// </summary>
        /// <returns></returns>
        public static List<Division> GetDivisions() { return Client.Instance.DivisionProvider.GetDivisions(); }

        /// <summary>
        /// Lists all the discussion posts for the specified deal
        /// </summary>
        /// <param name="dealId">Deal identifier</param>
        /// <returns></returns>
        public static List<Post> GetPosts(string dealId) { return Client.Instance.PostProvider.GetPosts(dealId); }

        /// <summary>
        /// Get a list of recent (or random) Groupon Says commentaries.
        /// </summary>
        /// <returns></returns>
        public static List<GrouponSaying> GetGrouponSayings() { return Client.GetGrouponSayings(10, false); }

        /// <summary>
        /// Gets a properly formatted Commission Junction referral URL for the given deal.
        /// </summary>
        /// <param name="publisherId">Publisher ID. This is the identifier of the publisher web site from which the consumer clicks on the link representing a Groupon deal. PID belongs to the publisher and can be retrieved from Commission Junction website. Navigate to Account->Web Site Settings section.</param>
        /// <param name="dealUrl">URL of the target Groupon deal.</param>
        /// <returns></returns>
        public static string GetCommissionJunctionUrl(string publisherId, string dealUrl) { return Client.GetCommissionJunctionUrl(publisherId, string.Empty, dealUrl); }

        /// <summary>
        /// Gets a properly formatted Commission Junction referral URL for the given deal.
        /// </summary>
        /// <param name="publisherId">Publisher ID. This is the identifier of the publisher web site from which the consumer clicks on the link representing a Groupon deal. PID belongs to the publisher and can be retrieved from Commission Junction website. Navigate to Account->Web Site Settings section.</param>
        /// <param name="shopperId">Shopper ID, also known as Sub ID. Commission Junction provides the capability for publishers to pass unique values in their affiliate link to identify where sale actions originate. Loyalty and Rewards publishers may use this parameter to pass a unique member ID in order to credit cash-back or points to its members.</param>
        /// <param name="dealUrl">URL of the target Groupon deal.</param>
        /// <returns></returns>
        public static string GetCommissionJunctionUrl(string publisherId, string shopperId, string dealUrl)
        {
            return Client.Instance.CommissionJunctionProvider.GetLink(dealUrl, publisherId, shopperId);
        }

        /// <summary>
        /// Get a list of recent (or random) Groupon Says commentaries.
        /// </summary>
        /// <param name="limit">Maximum of 20 at the same time</param>
        /// <param name="random">If set to true, will return a random collection of Groupon Says commentaries; otherwise response objects will be sorted by date of creation, most recent first.</param>
        /// <returns></returns>
        public static List<GrouponSaying> GetGrouponSayings(int limit, bool random) {
            if (limit > 20 || limit < 1) { limit = 20; }
            return Client.Instance.GrouponSayingsProvider.GetGrouponSayings(limit, random);
        }

        /// <summary>
        /// Forces all responses to be successful.
        /// </summary>
        public static bool ForceSuccessResponse { get; set; }

        internal DealProvider DealProvider { get; set; }

        internal DivisionProvider DivisionProvider { get; set; }

        internal PostsProvider PostProvider { get; set; }

        internal GrouponSayingsProvider GrouponSayingsProvider { get; set; }

        internal CommissionJunctionProvider CommissionJunctionProvider { get; set; }

        internal void OnClientException(List<string> messages)
        {
            if (Client.ClientException != null)
            {
                Client.ClientException(Client.Instance, new ClientExceptionEventArgs(messages));
            }
        }

        internal Response PostRequest(RequestBase request)
        {
            Response result = null;

            using (WebClient client = new WebClient())
            {

                client.Headers.Add("user-agent", Client.UserAgent);

                if (Client.Credentials != null)
                {
                    client.Credentials = Client.Credentials;
                }
                else
                {
                    client.UseDefaultCredentials = true;
                }

                if (Client.Proxy != null) { client.Proxy = Client.Proxy; }

                string xml = client.DownloadString(request.RESTCall);

                result = new Response(xml);

                if (result.HasErrors)
                {
                    OnClientException(result.Errors);
                }
            }

            return result;
        }

        /// <summary>
        /// Singleton
        /// </summary>
        internal static Client Instance { get { return InternalClient.m_instance; } }

        /// <summary>
        /// Implements thread-safe singleton pattern
        /// </summary>
        class InternalClient
        {
            internal static Client m_instance = new Client();
            static InternalClient()
            { }
        }

    }
}